\defmodule {XYListSeriesCollection}

This class extends
\externalclass{umontreal.iro.lecuyer.charts}{SSJXYSeriesCollection}.
It stores data used in a \texttt{XYLineChart} or in other related charts.
%
\texttt{XYListSeriesCollection} provides complementary tools to draw
 simple curves; for example, one may
add or remove plots series and modify plot style.
This class is linked with the JFreeChart \texttt{XYSeriesCollection} class to
store data plots,
and linked with the JFreeChart \texttt{XYLineAndShapeRenderer} to render the plot.
Each series must contain enough points to plot a nice curve.
It is recommended to use about 30 points. However, some rapidly
varying functions may require many more points. This class can be used to draw scatter plots.

\bigskip\hrule
\begin{code}
\begin{hide}
/*
 * Class:        XYListSeriesCollection
 * Description:  
 * Environment:  Java
 * Software:     SSJ 
 * Copyright (C) 2001  Pierre L'Ecuyer and Universite de Montreal
 * Organization: DIRO, Universite de Montreal
 * @author       
 * @since

 * SSJ is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License (GPL) as published by the
 * Free Software Foundation, either version 3 of the License, or
 * any later version.

 * SSJ is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * A copy of the GNU General Public License is available at
   <a href="http://www.gnu.org/licenses">GPL licence site</a>.
 */
\end{hide}
package umontreal.iro.lecuyer.charts;\begin{hide}

import   umontreal.iro.lecuyer.functions.MathFunction;
import   umontreal.iro.lecuyer.functionfit.SmoothingCubicSpline;
import   umontreal.iro.lecuyer.util.RootFinder;

import   org.jfree.data.xy.*;
import   org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;

import   cern.colt.list.DoubleArrayList;

import   java.util.Locale;
import   java.util.Formatter;
import   java.awt.Color;\end{hide}

public class XYListSeriesCollection  extends SSJXYSeriesCollection \begin{hide} {
   protected String[] marksType;   // marks on points (+, x, *...)
   protected String[] dashPattern; // line dashing (solid, dotted, densely dotted, loosely dotted,
                                 //               dashed, densely dashed, loosely dashed, only marks)
   protected String[] plotStyle;   // plot style (lines, curves...)
   private boolean autoCompletion = false;
\end{hide}
\end{code}

%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsubsection*{Constructors}

\begin{code}
   public XYListSeriesCollection() \begin{hide} {
      renderer = new XYLineAndShapeRenderer(true, false);
     // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
      seriesCollection = new XYSeriesCollection();
   }\end{hide}
\end{code}
\begin{tabb}
   Creates a new \texttt{XYListSeriesCollection} instance with an empty dataset.
\end{tabb}
\begin{code}

   public XYListSeriesCollection (double[][]... data) \begin{hide} {
      renderer = new XYLineAndShapeRenderer(true, false);
   //   ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
      seriesCollection = new XYSeriesCollection();

      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
      for (int i = 0; i < data.length; i ++) {

         if (data[i].length < 2)
            throw new IllegalArgumentException (
               "Unable to render the plot. data["+ i +"] contains less than two rows");

         for (int j = 0; j < data[i].length-1; j++)
            if (data[i][j].length != data[i][j+1].length)
               throw new IllegalArgumentException(
                  "data["+ i +"][" + j + "] and data["+ i +"]["+ (j+1) +"] must share the same length");

         for (int j = 1; j < data[i].length; j++) {
            XYSeries serie = new XYSeries(" ");
            for (int k = 0; k < data[i][0].length; k++)
               serie.add(data[i][0][k], data[i][j][k]);
            tempSeriesCollection.addSeries(serie);
         }
      }

      // set default colors
      for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
         renderer.setSeriesPaint(i, getDefaultColor(i));

      // set default plot style
      plotStyle = new String[tempSeriesCollection.getSeriesCount()];
      marksType = new String[tempSeriesCollection.getSeriesCount()];
      dashPattern = new String[tempSeriesCollection.getSeriesCount()];
      for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
         marksType[i] = " ";
         plotStyle[i] = "smooth";
         dashPattern[i] = "solid";
      }
   }\end{hide}
\end{code}
\begin{tabb}
   Creates a new \texttt{XYListSeriesCollection} instance with default
   parameters and given data series. The input parameter \texttt{data}
   represents a set of plotting data.

 For example, if one $n$-row matrix \texttt{data1} is given as argument,
 then the first row \texttt{data1}$[0]$ represents the
 $x$-coordinate vector, and every other row \texttt{data1}$[i],
   i=1,\ldots, n-1$, represents a $y$-coordinate set for the points.
  Therefore matrix \texttt{data1}$[i][j]$, $i=0,\ldots, n-1$,  corresponds
   to $n-1$ curves, all with the same $x$-coordinates.

  However, one may want to plot several curves with different $x$-coordinates.
  In that case, one should give the curves as matrices with two rows.
For examples, if the argument \texttt{data} is made of three 2-row matrices
\texttt{data1}, \texttt{data2} and \texttt{data3}, then they represents
 three different curves, \texttt{data*}$[0]$ being the $x$-coordinates,
 and  \texttt{data*}$[1]$  the $y$-coordinates of the curves.

 However, we may also consider the sets of points above not as part of curves,
but rather as several list of points.
\end{tabb}
\begin{htmlonly}
   \param{data}{series of point sets.}
\end{htmlonly}
\begin{code}

   public XYListSeriesCollection (double[][] data, int numPoints) \begin{hide} {
      renderer = new XYLineAndShapeRenderer(true, false);
     // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
      seriesCollection = new XYSeriesCollection();

      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
      if (data.length < 2)
         throw new IllegalArgumentException (
            "Unable to render the plot. data contains less than two rows");

      // n-1 curves: data[0] is x; data[i] is y for each curve
      for (int j = 1; j < data.length; j++) {
         XYSeries serie = new XYSeries(" ");
         for (int k = 0; k < numPoints; k++)
            serie.add(data[0][k], data[j][k]);
         tempSeriesCollection.addSeries(serie);
      }

      // set default colors
      for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
         renderer.setSeriesPaint(i, getDefaultColor(i));

      // set default plot style
      plotStyle = new String[tempSeriesCollection.getSeriesCount()];
      marksType = new String[tempSeriesCollection.getSeriesCount()];
      dashPattern = new String[tempSeriesCollection.getSeriesCount()];
      for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
         marksType[i] = " ";
         plotStyle[i] = "smooth";
         dashPattern[i] = "solid";
      }
   }\end{hide}
\end{code}
\begin{tabb}
   Creates a new \texttt{XYListSeriesCollection} instance with default
   parameters and given points \texttt{data}.
 If \texttt{data} is a $n$-row matrix,
 then the first row \texttt{data}$[0]$ represents the
 $x$-coordinate vector, and every other row \texttt{data}$[i],
   i=1,\ldots, n-1$, represents a $y$-coordinate set of points.
  Therefore, if the points represents curves to be plotted,
   \texttt{data}$[i][\ ]$, $i=0,\ldots, n-1$,  corresponds
   to $n-1$ curves, all with the same $x$-coordinates.
   Only the first \texttt{numPoints} of \texttt{data} will be considered
for each of the set of points.
\end{tabb}
\begin{htmlonly}
   \param{data}{series of point sets.}
   \param{numPoints}{Number of points to plot}
\end{htmlonly}
\begin{code}

   public XYListSeriesCollection (DoubleArrayList... data) \begin{hide} {
      renderer = new XYLineAndShapeRenderer(true, false);
     // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
      seriesCollection = new XYSeriesCollection ();
      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection )seriesCollection;

      XYSeries serie;
      double[] elements;
      int count = 0;
      DoubleArrayList temp;
      for(int i = 0; i < data.length; i++) {
         serie = new XYSeries(" ");

         temp = data[i].copy();  // deep copy
         temp.trimToSize();      // set capacity to the current size
         temp.quickSortFromTo(0, temp.size()-1);   // sort list in increasing order, simplify the next processings
         elements = temp.elements();

         int j = 0;
         int l = 0;
         while(j < elements.length) {
            while(j < elements.length && elements[j] == elements[l]) {
               j++;
               count++;
            }
            serie.add(elements[l], count);
            count = 0;
            l = j;
         }
         tempSeriesCollection.addSeries(serie);
      }

      // set default colors
      for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
         renderer.setSeriesPaint(i, getDefaultColor(i));
      }

      // set default plot style
      plotStyle = new String[tempSeriesCollection.getSeriesCount()];
      marksType = new String[tempSeriesCollection.getSeriesCount()];
      dashPattern = new String[tempSeriesCollection.getSeriesCount()];
      for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
         marksType[i] = " ";
         plotStyle[i] = "smooth";
         dashPattern[i] = "solid";
      }
   }\end{hide}
\end{code}
\begin{tabb}
   Creates a new \texttt{XYListSeriesCollection} instance with default parameters and given data.
   The input parameter represents a set of data plots, the constructor will count the occurrence number
   $Y$ of each value $X$ in the \texttt{DoubleArrayList}, and plot the point $(X, Y)$.
   Each \externalclass{cern.colt.list}{DoubleArrayList} variable corresponds to a curve on the chart.
\end{tabb}
\begin{htmlonly}
   \param{data}{series of point sets.}
\end{htmlonly}
\begin{code}

   public XYListSeriesCollection (XYSeriesCollection data) \begin{hide} {
      renderer = new XYLineAndShapeRenderer(true, false);
    //  ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
      seriesCollection = data;
      for(int i = 0; i < data.getSeriesCount(); i++) {
         XYSeries serie = data.getSeries(i);
      }

      // set default colors
      for(int i = 0; i < data.getSeriesCount(); i++) {
         renderer.setSeriesPaint(i, getDefaultColor(i));
      }

      // set default plot style
      plotStyle = new String[data.getSeriesCount()];
      marksType = new String[data.getSeriesCount()];
      dashPattern = new String[data.getSeriesCount()];
      for(int i = 0; i < data.getSeriesCount(); i++) {
         marksType[i] = " ";
         plotStyle[i] = "smooth";
         dashPattern[i] = "solid";
      }
   }\end{hide}
\end{code}
\begin{tabb}
   Creates a new \texttt{XYListSeriesCollection} instance with default parameters and given data series.
   The input parameter represents a set of plotting data.
   Each series of the given collection corresponds to a curve on the plot.
\end{tabb}
\begin{htmlonly}
   \param{data}{series of point sets.}
\end{htmlonly}


%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsubsection*{Data control methods}

\begin{code}

   public int add (double[] x, double[] y) \begin{hide} {
      if (x.length != y.length)
         throw new IllegalArgumentException("x and y must have the same length");
      return add (x, y, x.length);
   }\end{hide}
\end{code}
\begin{tabb}
   Adds a data series into the series collection. Vector \texttt{x} represents
   the $x$-coordinates and vector \texttt{y} represents the $y$-coordinates of
   the series.
\end{tabb}
\begin{htmlonly}
   \param{x}{$x_i$ coordinates.}
   \param{y}{$y_i$ coordinates.}
   \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
\end{htmlonly}
\begin{code}

   public int add (double[] x, double[] y, int numPoints) \begin{hide} {
      XYSeries serie = new XYSeries(" ");
      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection )seriesCollection;
      serie.setNotify(true);
      if ((x.length < numPoints) ||(y.length < numPoints))
         throw new IllegalArgumentException("numPoints > length of x or y");
      for (int i = 0; i < numPoints; i++)
         serie.add(x[i], y[i]);
      tempSeriesCollection.addSeries(serie);

      // color
      int j = tempSeriesCollection.getSeriesCount()-1;
      renderer.setSeriesPaint(j, getDefaultColor(j));

      int co = tempSeriesCollection.getSeriesCount();
      String[] newPlotStyle = new String[co];
      String[] newMarksType = new String[co];
      String[] newDashPattern = new String[co];
      for(j = 0; j < co - 1; j++) {
         newPlotStyle[j] = plotStyle[j];
         newMarksType[j] = marksType[j];
         newDashPattern[j] = dashPattern[j];
      }

      newPlotStyle[j] = "smooth";
      newMarksType[j] = " ";
      newDashPattern[j] = "solid";
      plotStyle = newPlotStyle;
      marksType = newMarksType;
      dashPattern = newDashPattern;

      return tempSeriesCollection.getSeriesCount()-1;
   }\end{hide}
\end{code}
\begin{tabb}
   Adds a data series into the series collection. Vector \texttt{x} represents
   the $x$-coordinates and vector \texttt{y} represents the $y$-coordinates of
   the series. Only \emph{the first} \texttt{numPoints} of \texttt{x}
   and \texttt{y} will be added to the new series.
\end{tabb}
\begin{htmlonly}
   \param{x}{$x_i$ coordinates.}
   \param{y}{$y_i$ coordinates.}
   \param{numPoints}{Number of points to add}
   \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
\end{htmlonly}
\begin{code}

   public int add (double[][] data) \begin{hide} {
      return add (data, data[0].length);
   }\end{hide}
\end{code}
\begin{tabb}
   Adds a data series into the series collection. The input format of
 \texttt{data} is described in constructor
\texttt{XYListSeriesCollection(double[][] data)}.
\end{tabb}
\begin{htmlonly}
   \param{data}{input data.}
   \return{Integer that represent the number of point sets added to the current dataset.}
\end{htmlonly}
\begin{code}

   public int add (double[][] data, int numPoints) \begin{hide} {
      XYSeriesCollection tempSeriesCollection =
          (XYSeriesCollection) seriesCollection;
      int n = tempSeriesCollection.getSeriesCount();

      if (data.length < 2)
         throw new IllegalArgumentException(
            "Unable to render the plot. data contains less than two rows");

      for (int j = 0; j < data.length; j++)
         if (data[j].length < numPoints)
            throw new IllegalArgumentException(
               "data[" + j + "] has not enough points");

      for (int j = 1; j < data.length; j++) {
         XYSeries serie = new XYSeries(" ");
         serie.setNotify(true);
         for (int k = 0; k < numPoints; k++)
            serie.add(data[0][k], data[j][k]);
         tempSeriesCollection.addSeries(serie);
      }

      // color
      for(int j = n; j < tempSeriesCollection.getSeriesCount(); j++)
         renderer.setSeriesPaint(j, getDefaultColor(j));

      String[] newPlotStyle = new String[tempSeriesCollection.getSeriesCount()];
      String[] newMarksType = new String[tempSeriesCollection.getSeriesCount()];
      String[] newDashPattern = new String[tempSeriesCollection.getSeriesCount()];
      for (int j = 0; j < n; j++) {
         newPlotStyle[j] = plotStyle[j];
         newMarksType[j] = marksType[j];
         newDashPattern[j] = dashPattern[j];
      }

      for(int j = n; j < tempSeriesCollection.getSeriesCount(); j++) {
         newPlotStyle[j] = "smooth";
         newMarksType[j] = " ";
         newDashPattern[j] = "solid";
      }
      plotStyle = newPlotStyle;
      marksType = newMarksType;
      dashPattern = newDashPattern;

      return (tempSeriesCollection.getSeriesCount()-n);
   }\end{hide}
\end{code}
\begin{tabb}
   Adds  data series into the series collection. The input format of
 \texttt{data} is described in constructor
\texttt{XYListSeriesCollection(double[][] data)}.
  Only \emph{the first} \texttt{numPoints} of \texttt{data}
 (the first \texttt{numPoints} columns of the matrix)
  will be added to each new series.
\end{tabb}
\begin{htmlonly}
   \param{data}{input data.}
   \param{numPoints}{Number of points to add for each new series}
   \return{Integer that represent the number of point sets added to the current dataset.}
\end{htmlonly}
\begin{code}

   public int add (DoubleArrayList data) \begin{hide} {
      XYSeries serie = new XYSeries(" ");
      DoubleArrayList temp = data.copy();  // deep copy
      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;

      temp.trimToSize();      // set capacity to the current size
      temp.quickSortFromTo(0, temp.size()-1);   // sort list in increasing order, simplify the next processings
      double[] elements = temp.elements();

      int count = 0;
      int j = 0;
      int l = 0;
      while(j < elements.length) {
         while(j < elements.length && elements[j] == elements[l]) {
            j++;
            count++;
         }
         serie.add(elements[l], count);
         count = 0;
         l = j;
      }
      tempSeriesCollection.addSeries(serie);

      // color
      j = tempSeriesCollection.getSeriesCount()-1;
      renderer.setSeriesPaint(j, getDefaultColor(j));

      String[] newPlotStyle = new String[tempSeriesCollection.getSeriesCount()];
      String[] newMarksType = new String[tempSeriesCollection.getSeriesCount()];
      String[] newDashPattern = new String[tempSeriesCollection.getSeriesCount()];
      for(j = 0; j < tempSeriesCollection.getSeriesCount()-1; j++) {
         newPlotStyle[j] = plotStyle[j];
         newMarksType[j] = marksType[j];
         newDashPattern[j] = dashPattern[j];
      }

      newPlotStyle[j] = "smooth";
      newMarksType[j] = " ";
      newDashPattern[j] = "solid";
      plotStyle = newPlotStyle;
      marksType = newMarksType;
      dashPattern = newDashPattern;

      return tempSeriesCollection.getSeriesCount()-1;
   }\end{hide}
\end{code}
\begin{tabb}
   Adds a data series into the series collection. The input format of
 \texttt{data} is described in constructor
\texttt{XYListSeriesCollection (DoubleArrayList... data)}.
\end{tabb}
\begin{htmlonly}
   \param{data}{data series.}
   \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
\end{htmlonly}
\begin{code}

   public String getName (int series) \begin{hide} {
      return (String)((XYSeriesCollection )seriesCollection).getSeries(series).getKey();
   }\end{hide}
\end{code}
\begin{tabb}
   Gets the current name of the selected series.
\end{tabb}
\begin{htmlonly}
   \param{series}{series index.}
   \return{current name of the series.}
\end{htmlonly}
\begin{code}

   public void setName (int series, String name) \begin{hide} {
      if(name == null)
         name = " ";
      ((XYSeriesCollection)seriesCollection).getSeries(series).setKey(name);
   }\end{hide}
\end{code}
\begin{tabb}
   Sets the name of the selected series.
\end{tabb}
\begin{htmlonly}
   \param{series}{series index.}
   \param{name}{point set new name.}
\end{htmlonly}


\newpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsubsection*{Rendering methods}

\begin{code}

   public void enableAutoCompletion() \begin{hide} {
      this.autoCompletion = true;
   }\end{hide}
\end{code}
\begin{tabb}
   Enables the auto completion option. When this parameter
   is enabled, straight lines are used to approximate points on the
   chart bounds if the method isn't able to display all points,
   because the user defined bounds are smaller than the
   most significant data point coordinate, for instance.
   It does not extrapolate the point sets, but simply estimates
   point coordinates on the curve at bound positions for a better visual rendering.
\end{tabb}
\begin{code}

   public void disableAutoCompletion() \begin{hide} {
      this.autoCompletion = false;
   }\end{hide}
\end{code}
\begin{tabb}
   Disables auto completion option. Default status is \texttt{disabled}.
\end{tabb}
\begin{code}

   public String getMarksType (int series) \begin{hide} {
      return marksType[series];
   }\end{hide}
\end{code}
\begin{tabb}
   Returns the mark type associated with the \texttt{series}th data series.
\end{tabb}
\begin{htmlonly}
   \param{series}{series index.}
   \return{mark type.}
\end{htmlonly}
\begin{code}

   public void setMarksType (int series, String marksType) \begin{hide} {
      this.marksType[series] = marksType;
   }\end{hide}
\end{code}
\begin{tabb}
   Adds marks on the points of a data series.
   It is possible to use any of the marks provided by the TikZ package,
   some of which are ``\texttt{*}'', ``\texttt{+}'' and ``\texttt{x}''.
   A blank character, used by default, disables marks.
   The PGF/TikZ documentation provides more information about placing marks on plots.
\end{tabb}
\begin{htmlonly}
   \param{series}{series index.}
   \param{marksType}{mark type.}
\end{htmlonly}
\begin{code}

   public String getDashPattern (int series) \begin{hide} {
      return dashPattern[series];
   }\end{hide}
\end{code}
\begin{tabb}
   Returns the dash pattern associated with the \texttt{series}th data series.
\end{tabb}
\begin{htmlonly}
   \param{series}{series index.}
   \return{mark type.}
\end{htmlonly}
\begin{code}

   public void setDashPattern (int series, String dashPattern) \begin{hide} {
      this.dashPattern[series] = dashPattern;
      if (dashPattern.equals("only marks")) {
          ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series, false);
          ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series, true);
      } else {
          ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series, true);
          ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series, false);
      }
   }\end{hide}
\end{code}
\begin{tabb}
Selects dash pattern for a data series. It is possible to use all the dash
options provided by the TikZ package: ``\texttt{solid}'', ``\texttt{dotted}'',
``\texttt{densely dotted}'', ``\texttt{loosely dotted}'', ``\texttt{dashed}'',
``\texttt{densely dashed}'', ``\texttt{loosely dashed}'' and
``\texttt{only marks}''. If ``\texttt{only marks}" is chosen, then method
\method{setMarksType}{} must be called to choose the marks
(which are blank by default).
\end{tabb}
\begin{htmlonly}
   \param{series}{series index.}
   \param{dashPattern}{dash style.}
\end{htmlonly}
\begin{code}

   public String getPlotStyle (int series) \begin{hide} {
      return plotStyle[series];
   }\end{hide}
\end{code}
\begin{tabb}
   Gets the current plot style for the selected series.
\end{tabb}
\begin{htmlonly}
   \param{series}{series index.}
   \return{current plot style.}
\end{htmlonly}
\begin{code}

   public void setPlotStyle (int series, String plotStyle) \begin{hide} {
      this.plotStyle[series] = plotStyle;
   }\end{hide}
\end{code}
\begin{tabb}
   Selects the plot style for a given series. It is possible to use all the
   plot options provided by the TikZ package. Some of which are:
   ``\texttt{sharp plot}'', which joins points with straight lines,
   ``\texttt{smooth}'', which joins points with a smoothing curve,
   ``\texttt{only marks}'', which does not join points, etc.
   The PGF/TikZ documentation provides more information about smooth plots,
     sharp plots and comb plots.
\end{tabb}
\begin{htmlonly}
   \param{series}{series index.}
   \param{plotStyle}{plot style.}
\end{htmlonly}
\begin{code}

   public String toLatex (double XScale, double YScale,
                          double XShift, double YShift,
                          double xmin, double xmax,
                          double ymin, double ymax)\begin{hide} {

      // Calcule les bornes reelles du graphique, en prenant en compte la position des axes
      xmin = Math.min(XShift, xmin);
      xmax = Math.max(XShift, xmax);
      ymin = Math.min(YShift, ymin);
      ymax = Math.max(YShift, ymax);

      Formatter formatter = new Formatter(Locale.US);
      XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
      double XEPSILON = (1.0E-4/XScale)+XShift;
      double YEPSILON = (1.0E-4/YScale)+YShift;
      boolean outOfBounds = false;
      MathFunction[] spline = null;
      double[] xBounds = getRangeBounds();
      double[] yBounds = getDomainBounds();
      double x, y;
// Smoothing splines, consulter  ref: QA278.2 G74, QA278.2 T35, QA278.2 E87

//       if(xBounds[0] < xmin || xBounds[1] > xmax || yBounds[0] < ymin || yBounds[1] > ymax) {
//          // on sait qu'il y a des points qui vont sortir du chart
//          // initialisation d'une spline pour chaque courbe
//          spline = new SmoothingCubicSpline[seriesCollection.getSeriesCount()];
//          for(int i = 0; i<seriesCollection.getSeriesCount(); i++)
//             spline[i] = new SmoothingCubicSpline(  (seriesCollection.getSeries(i).toArray())[0],
//                                                    (seriesCollection.getSeries(i).toArray())[1], 0.5);
//       }

      // on sait qu'il y a des points qui vont sortir du chart
      // initialisation d'une spline pour chaque courbe
      if (true) {
         spline = new SmoothingCubicSpline[tempSeriesCollection.getSeriesCount()];
         for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
            spline[i] = new SmoothingCubicSpline((tempSeriesCollection.getSeries(i).toArray())[0],
                                                 (tempSeriesCollection.getSeries(i).toArray())[1], 1);
      } else {
         spline = new AffineFit[tempSeriesCollection.getSeriesCount()];
         for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
            spline[i] = new AffineFit((tempSeriesCollection.getSeries(i).toArray())[0],
                                      (tempSeriesCollection.getSeries(i).toArray())[1]);
      }

      for(int i = tempSeriesCollection.getSeriesCount()-1; i >= 0; i--) {
         XYSeries temp = tempSeriesCollection.getSeries(i);

         if (temp.getItemCount() < 2)
            throw new IllegalArgumentException("Unable to plot series " + i +
                                               ": this series must have two points at least");

         Color color = (Color)renderer.getSeriesPaint(i);
         String colorString = detectXColorClassic(color);
         if( colorString == null) {
            colorString = "color"+i;
            formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
                              colorString, color.getRed()/255.0, color.getGreen()/255.0, color.getBlue()/255.0);
         }

         // Cas particulier pour le premier point, on doit savoir si il est dans le chart ou pas
         if (  temp.getX(0).doubleValue() >= xmin && temp.getX(0).doubleValue() <= xmax &&
               temp.getY(0).doubleValue() >= ymin && temp.getY(0).doubleValue() <= ymax) {
            outOfBounds = false;
            formatter.format( "\\draw [%s, color=%s, mark=%s, style=%s] plot coordinates {%%%n",
                              plotStyle[i], colorString, marksType[i], dashPattern[i]);
         }
         else {
            outOfBounds = true;
            formatter.format("%% ");
         }
         formatter.format("(%.2f,%.4f)",   (temp.getX(0).doubleValue()-XShift)*XScale,
                                              (temp.getY(0).doubleValue()-YShift)*YScale);
         formatter.format(" %%   (%f,  %f)%n", temp.getX(0).doubleValue(), temp.getY(0).doubleValue());

         // Cas general
         for(int j = 1; j < temp.getItemCount(); j++) {
            double[] result;
            if (!outOfBounds) { //on est dans le chart
               result = evalLimitValues(xmin, xmax, ymin, ymax, XEPSILON, YEPSILON, spline[i], temp, j, false);
               // on regarde si on ne sort pas du chart, si c'est le cas on evalue le point en limite
               if (result != null) { // le point courant n'est pas dans le chart, on sort donc du chart
                  outOfBounds = true;
                  if (autoCompletion)
                     formatter.format("(%.2f,%.4f) %%%n", (result[0]-XShift)*XScale, (result[1]-YShift)*YScale);
                  formatter.format("}%%%n%% ");
               }
            }
            else { // le point precedent etait hors du chart
               if (  temp.getX(j).doubleValue() >= xmin && temp.getX(j).doubleValue() <= xmax &&
                     temp.getY(j).doubleValue() >= ymin && temp.getY(j).doubleValue() <= ymax) {
                     // on rentre dans le chart, il faut evaluer le point en limite
                  j = j-1;
                  result = evalLimitValues(xmin, xmax, ymin, ymax, XEPSILON, YEPSILON, spline[i], temp, j, true);
                  // ici result ne peut pas etre null
                  formatter.format( ";%%%n\\draw [%s, color=%s, mark=%s, style=%s] plot coordinates {%%%n",
                                    plotStyle[i], colorString, marksType[i], dashPattern[i]);
                  if (autoCompletion)
                     formatter.format("(%.2f,%.4f) %%%n ", (result[0]-XShift)*XScale, (result[1]-YShift)*YScale);
                  formatter.format("%% ");
                  outOfBounds = false;
               }
               else {
                  formatter.format("%% ");
                  // on les donnees sont toujours hors du chart
               }
            }
            /* on affiche les coordonnees du point quoiqu'il arrive,
            si celui ci est hors du chart alors la balise de commentaire a ete deja place */
            formatter.format("(%.2f,%.4f)",   (temp.getX(j).doubleValue()-XShift)*XScale,
                                              (temp.getY(j).doubleValue()-YShift)*YScale);
            if(j == temp.getItemCount()-1)
               formatter.format("}");
            formatter.format(" %%   (%f,  %f)%n", temp.getX(j).doubleValue(), temp.getY(j).doubleValue());
//            formatter.format(" %%%n");
         }
         formatter.format(" node[right] {%s};%n", (String)temp.getKey());
      }
      return formatter.toString();
   }


   /**
    * Compute x and y to chart limit bounds for extra-bounded points
    *
    * @param   xmin     lower bound for x coordinates
    * @param   xmax     upper bound for x coordinates
    * @param   ymin     lower bound for y coordinates
    * @param   ymax     upper bound for y coordinates
    * @param   XEPSILON increment step size for x coordinates
    * @param   YEPSILON increment step size for y coordinates
    * @param   spline   sline used to approximate points
    * @param   temp     current series
    * @param   numPoint point index in the current series
    * @param   sens     direction of the in chart last point
    *                    true  : point numPoint+1 is in the chart
    *                    false : point numPoint-1 is in the chart
    *
    * @return           x and y coordinates on the chart bounds.
    */
   private static double[] evalLimitValues(double xmin, double xmax, double ymin, double ymax, double XEPSILON, double YEPSILON, MathFunction spline, XYSeries temp, int numPoint, boolean sens) {
      int j = numPoint;
      int k = 0;
      double x, y;
      if(sens)
         k = j+1;
      else
         k = j-1;
      if(temp.getX(j).doubleValue() < xmin) {// Hors du chart mais on etait dans le chart au point precedent
         x = xmin;
         y = spline.evaluate(xmin); // spline puis evaluer en xmin
         while(y<ymin) {
            x += XEPSILON;
            y = spline.evaluate(x);
         }  // evaluer un x>xmin tantque y<ymin, y peut etre superieur a ymin car le point precedent est dans le chart
         while(y > ymax) {
            x += XEPSILON;
            y = spline.evaluate(x);
         }  // evaluer un x en ymax avec x > xmin
      }
      else if(temp.getX(j).doubleValue() > xmax) {
         x = xmax;
         y = spline.evaluate(xmax);
         while(y<ymin) {
            x -= XEPSILON;
            y = spline.evaluate(x);
         }  // evaluer un x<xmax tantque y<ymin
         while(y > ymax) {
            x -= XEPSILON;
            y = spline.evaluate(x);
         }  // evaluer un x<xmax tantque y>ymax
      }
      else if(temp.getY(j).doubleValue() < ymin) {
         y = ymin;
         x = evaluateX(spline, y, temp.getX(j).doubleValue(), temp.getX(k).doubleValue());// spline puis evaluer en ymin avec x ente xValue(ptCourant) et xValue(ptCourant-1)
         while(x < xmin) {
            y += YEPSILON;
            x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
         }
         while(x > xmax) {
           y += YEPSILON;
           x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
         }
      }
      else if(temp.getY(j).doubleValue() > ymax) {
         y = ymax;
         x = evaluateX(spline, y, temp.getX(j).doubleValue(), temp.getX(k).doubleValue());// spline puis evaluer en ymax avec x ente xValue(ptCourant) et xValue(ptCourant-1)
         while(x < xmin) {
            y -= YEPSILON;
            x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
         }
         while(x > xmax) {
            y -= YEPSILON;
            x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
         }
      }
      else
         return null;
      double[] retour = new double[2];
      retour[0] = x;
      retour[1] = y;
      return retour;
   }


   private static double evaluateX (final MathFunction spline, final double y, double xPrincipal, double xAnnexe) {
      final MathFunction xFunction = new MathFunction () {
         public double evaluate (double t) {
            return spline.evaluate(t) - y;
         }
      };
      return RootFinder.brentDekker (xPrincipal, xAnnexe-1.0E-6, xFunction, 1e-6);
   }


   private class AffineFit implements MathFunction{

      double[] x;
      double[] y;

      public AffineFit(double[] x, double[] y) {
         this.x =x;
         this.y =y;
      }

      public double evaluate(double t) {
         int i = 0;
         if (t <= x[0])
            return y[0];
         while (i < x.length && t > x[i])
            i++;
         i--;
         if (i == x.length)
            return x[x.length-1];

         return y[i] + ((t - x[i]) / (x[i+1] - x[i])) * (y[i+1] - y[i]);
      }
   }
}\end{hide}
\end{code}
